/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 mana_leech.cpp

	$Header: /heroes4/mana_leech.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )
#include "mana_leech.h"

#include "adaptor_handler.h"
#include "battlefield.h"
#include "bound_handler.h"
#include "can_cast.h"
#include "combat_actor_model_cache.h"
#include "combat_creature.h"
#include "direction.h"
#include "enum_operations.h"
#include "external_string.h"
#include "format_string.h"
#include "function_handler.h"
#include "missile_type.h"
#include "move_missile.h"
#include "pixel_24.h"
#include "spell.h"

static t_external_string const k_combat_action_mana_leech_ability( "combat_action.mana_leech_ability" );

// ordinarily, this sort of code would be part of t_battlefield, but
// t_battlefield is getting entirely too large.
// --------------------------------------------------------------------------
// find a target for mana leech
// --------------------------------------------------------------------------
static
t_combat_creature* find_leech_target( t_battlefield& battlefield, t_combat_creature& caster )
{
	t_combat_creature_list::iterator index = battlefield.creatures_begin();
	t_combat_creature_list::iterator end = battlefield.creatures_end();
	t_combat_creature*               target = 0;
	t_combat_creature*               creature;
	int                              resistance;
	int                              best_resistance;
	int                              distance;
	int                              best_distance;

	for (; index != end; index++)
	{
		creature = index->get();
		if ( caster.get_controller() == creature->get_controller() )
			continue;
		if ( creature->get_number() == 0)
			continue;
		if ( creature->get_spell_points() < 1 )
			continue;
		if ( !can_affect( caster, k_spell_mana_leech, *creature ))
			continue;
		if ( !battlefield.can_see( caster, *creature ))
			continue;
		resistance = creature->get_magic_resistance( k_spell_mana_leech );
		distance = caster.get_edge_distance( *creature );
		if ( target != 0 )
		{
			if ( resistance > best_resistance )
				continue;
			if ( resistance == best_resistance && distance > best_distance)
				continue;
		}
		best_resistance = resistance;
		best_distance = distance;
		target = creature;
	}
	return target;
}

// --------------------------------------------------------------------------
// check if creature knows any spells
// --------------------------------------------------------------------------
static bool knows_any_spells( t_combat_creature const& creature )
{
	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
		if (creature.knows_spell( spell ))
			return true;
	return false;
}

// --------------------------------------------------------------------------
// find a recipient for mana leech
// --------------------------------------------------------------------------
static
t_combat_creature* find_leech_recipient( t_battlefield& battlefield, t_combat_creature& caster )
{
	t_combat_creature_list::iterator index = battlefield.creatures_begin();
	t_combat_creature_list::iterator end = battlefield.creatures_end();
	t_combat_creature*               target = 0;
	t_combat_creature*               creature;
	float                            spell_points_percentage;
	float                            lowest_percentage;
	int                              distance;
	int                              best_distance;

	for (; index != end; index++)
	{
		creature = index->get();
		if ( caster.get_controller() != creature->get_controller() )
			continue;
		if ( creature->get_number() == 0)
			continue;
		if ( creature->get_maximum_spell_points() < 1)
			continue;
		if ( !knows_any_spells( *creature ))
			continue;
		spell_points_percentage = float(creature->get_spell_points()) 
								  / creature->get_maximum_spell_points();
		distance = caster.get_edge_distance( *creature );
		if ( target != 0 )
		{
			if ( spell_points_percentage > lowest_percentage )
				continue;
			if ( spell_points_percentage == lowest_percentage && distance > best_distance)
				continue;
		}
		lowest_percentage = spell_points_percentage;
		best_distance = distance;
		target = creature;
	}
	return target;
}

// --------------------------------------------------------------------------
// handle gift of mana
// --------------------------------------------------------------------------
static
void receive_mana( t_combat_creature_ptr caster, t_map_point_2d point, int amount )
{
	t_battlefield&	   battlefield = caster->get_battlefield();
	t_map_point_2d     cell_point = point >> k_battlefield_subcell_shift;
	t_combat_creature* target = battlefield.get_creature( cell_point );

	target->change_spell_points( amount );
	battlefield.add_text( *target, format_string( "+%i", amount ), target->get_message_color(),
		                  k_message_icon_spell_points );
	battlefield.update_state();
}

// --------------------------------------------------------------------------
// handle leech impact
// --------------------------------------------------------------------------
static 
void leech_impact( t_combat_creature_ptr victim, t_map_point_2d, t_combat_action_message message )
{
	t_battlefield&	   battlefield = victim->get_battlefield();
	t_combat_creature& caster = *battlefield.get_acting_creature();
	t_combat_creature* recipient;
	int                stolen = caster.get_spell_power( k_spell_mana_leech );
		
	if (stolen > victim->get_spell_points())
		stolen = victim->get_spell_points();
	victim->change_spell_points( -stolen );
	recipient = find_leech_recipient( battlefield, caster );
	if (recipient == 0)
	{
		battlefield.update_state();
		return;
	}

	t_direction					  direction = get_direction( caster, *recipient );
	t_missile_handler             handler;

	battlefield.set_current_direction( caster, direction );
	caster.set_wait_animation(); // force direction to NE, SE, SW, or NW.
	handler = add_3rd_argument( function_3_handler( receive_mana ), stolen );
	battlefield.launch_missile( k_missile_mana, caster, caster.get_spell_origin(),
		                        recipient->get_body_center(), handler, message );
}

// --------------------------------------------------------------------------
// execute leech
// --------------------------------------------------------------------------
static 
void execute_leech( t_combat_creature& caster, t_combat_creature* victim, t_combat_action_message message )
{
	t_battlefield&				  battlefield = caster.get_battlefield();
	t_missile_handler             handler;
	int                           stolen = caster.get_spell_power( k_spell_mana_leech );
		
	if (!battlefield.check_resistance( caster, *victim, k_spell_mana_leech ))
	{
		battlefield.update_state();
		return;
	}
	if (stolen > victim->get_spell_points())
		stolen = victim->get_spell_points();
	handler = add_3rd_argument( function_3_handler( leech_impact ), message );
	battlefield.launch_missile( k_missile_mana, *victim, victim->get_body_center(),
		                        caster.get_body_center(), handler, message );
	battlefield.add_text( *victim, format_string( "-%i", stolen ), victim->get_message_color(),
		                  k_message_icon_spell_points );
}

// --------------------------------------------------------------------------
// begin leech
// --------------------------------------------------------------------------
bool begin_mana_leech( t_battlefield& battlefield )
{
	t_combat_creature&		  caster = *battlefield.get_acting_creature();
	t_combat_creature*		  victim;
	t_combat_creature_handler handler;

	if (caster.has_leeched())
		return false;

	caster.set_leeched( true );
	victim = find_leech_target( battlefield, caster );
	if (victim == 0)
		return false;

	t_combat_creature_handler end_handler;
	t_combat_action_message   message( caster, k_combat_action_mana_leech_ability );

	handler = add_2nd_argument( add_3rd_argument( function_3_handler( execute_leech ), message), victim );
	end_handler = discard_argument<t_combat_creature&>( bound_handler( battlefield,
		          &t_battlefield::update_state ));
	battlefield.begin_spell( victim, handler, end_handler, message );
	return true;
}
